package copy

import (
	"errors"
	"fmt"
	"io"
	"sync"
)

const (
	// 错误操作
	ERROR_OP_IGNOREALL    = "IgnoreAll"
	ERROR_OP_INTERRUPT    = "Interrupt"
	ERROR_OP_IGNORE       = "Ignore"
	ERROR_OP_OVERWRITE    = "Overwrite"
	ERROR_OP_OVERWRITEALL = "OverwriteAll"
	ERROR_OP_STOP         = "Stop"

	// 错误处理
	ERROR_HANDLE_CONTINUE = "continue"
	ERROR_HANDLE_DO       = "do"
	ERROR_HANDLE_STOP     = "stop"

	// 错误类型
	ERROR_TYPE_NO            = 0
	ERROR_TYPE_DST_EXIST     = 1
	ERROR_TYPE_INVILD_LINK   = 2
	ERROR_TYPE_CREATE_LINK_F = 3
	ERROR_TYPE_CREATE_DIR_F  = 4
	ERROR_TYPE_CREARE_FILE_F = 5
	ERROR_TYPE_OPEN_FILE_F   = 6
	ERROR_TYPE_WRITE_FILE_F  = 7
	ERROR_TYPE_SRC_TYPE      = 8
)

type Copyer struct {
	totalCount int64
	totalSize  int64

	srcDir string //
	dstDir string
	items  []string // 需要拷贝的文件，支持多个文件同时拷贝

	// 复制操作失败操作码
	errorOp   string
	errorType int         // 错误类型
	errMsg    error       // 错误信息
	newOpChan chan bool   // 当错误错误操作是中断时，将会等待外部的命令，通过这个通道等待
	lock      *sync.Mutex // 通道锁

	doneCount int64  // 已经复制的文件数量
	doneSize  int64  // 已经复制的文件大小
	doingName string // 正在计算／复制的文件名称

	stopFlag bool // 停止时设置, 在错误处理函数中修改设置这个值
	IsDoing  bool

	// 复制时使用到的buf，
	// 代码来着io。Copy ，自己实现是为了精确统计doneSize
	copyBuf []byte
}

func NewCopyer(srcDir, dstDir string, items []string, errorOp string) (copyer *Copyer, err error) {
	err = copyCheck(srcDir, dstDir, items)
	if err != nil {
		return nil, err
	}

	copyer = new(Copyer)
	copyer.srcDir = srcDir
	copyer.dstDir = dstDir
	copyer.items = items
	copyer.errorOp = errorOp
	copyer.newOpChan = make(chan bool, 1)
	copyer.lock = new(sync.Mutex)

	return copyer, nil
}

// 实时生效的一个标记
// 如果命令通道是空的，并且是在运行中,将会向命令通道中写入命令
func (self *Copyer) SetErrorOp(op string) error {
	self.lock.Lock()
	defer self.lock.Unlock()

	// 不同类型的错误，允许的操作是不同的
	switch self.errorType {
	case ERROR_TYPE_DST_EXIST, ERROR_TYPE_NO:

	default:
		// 目标文件创建失败或者写入失败时，只允许跳过，或者停止复制
		if op != ERROR_OP_IGNORE && op != ERROR_OP_IGNOREALL && op != ERROR_OP_STOP {
			return errors.New("error op")
		}
	}
	self.errorOp = op
	if self.IsDoing && len(self.newOpChan) == 0 {
		self.newOpChan <- true
	}
	return nil
}

func (self *Copyer) handleError(err error) string {
	switch self.errorOp {
	case ERROR_OP_IGNORE:
		self.errorOp = ERROR_OP_INTERRUPT
		return ERROR_HANDLE_CONTINUE
	case ERROR_OP_IGNOREALL:
		return ERROR_HANDLE_CONTINUE
	case ERROR_OP_INTERRUPT:
		<-self.newOpChan
		return self.handleError(err)
	case ERROR_OP_OVERWRITE:
		self.errorOp = ERROR_OP_INTERRUPT
		return ERROR_HANDLE_DO
	case ERROR_OP_OVERWRITEALL:
		return ERROR_HANDLE_DO
	case ERROR_OP_STOP:
		self.stopFlag = true
		return ERROR_HANDLE_STOP
	default:
		fmt.Println("error")
	}
	return ""
}

func (self *Copyer) copy(dst io.Writer, src io.Reader) (written int64, err error) {
	if self.copyBuf == nil {
		self.copyBuf = make([]byte, 32*1024)
	}
	for {
		nr, er := src.Read(self.copyBuf)
		if nr > 0 {
			nw, ew := dst.Write(self.copyBuf[0:nr])
			if nw > 0 {
				written += int64(nw)
				self.doneSize += written
			}
			if ew != nil {
				err = ew
				break
			}
			if nr != nw {
				err = io.ErrShortBuffer
				break
			}
		}
		if er == io.EOF {
			break
		}
		if er != nil {
			err = er
			break
		}
	}
	return written, err
}
